package com.puwang.openglbasic;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.opengl.GLES20;
import android.opengl.Matrix;

//三角形
//主要工作分3步：maPositionHandle
//获取对应属性变量的引用、通过引用将缓冲中的数据传入管线、启用传入的数据
/**
 1、加载顶点着色器的脚本内容
 读取
 2、基于顶点着色器与片元着色器创建程序
 你需要至少一个顶点着色器来绘制一个形状，以及一个片段着色器为该形状上色。这些着色器必须被编译然后添加到一个OpenGL ES Program当中
 3、加载顶点着色器和片元着色器
 4、向程序中加入顶点着色器和片元着色器
 */
public class Triangle
{
    public static float[] mProjMatrix = new float[16];//4x4矩阵 投影用   投影矩阵
    public static float[] mVMatrix = new float[16];//摄像机位置朝向9参数矩阵   摄像机参数矩阵
    public static float[] mMVPMatrix;//最后起作用的总变换矩阵

    int mProgram;//自定义渲染管线程序id
    int muMVPMatrixHandle;//总变换矩阵一致变量的引用id
    int maPositionHandle; //顶点位置属性引用id，用来存储从着色器中获取的对应属性变量的引用值
    int maColorHandle; //顶点颜色属性引用id
    String mVertexShader;//顶点着色器
    String mFragmentShader;//片元着色器
    static float[] mMMatrix = new float[16];//具体物体的3D变换矩阵，移动旋转矩阵，包括旋转、平移、缩放

    FloatBuffer   mVertexBuffer;//顶点坐标数据缓冲
    FloatBuffer   mColorBuffer;//顶点着色数据缓冲
    int vCount=0;//顶点数量
    float xAngle=0;//绕x轴旋转的角度
    public Triangle(MyTDView mv)
    {
        //初始化顶点坐标与着色数据
        initVertexData();
        //初始化shader
        initShader(mv);
    }

    //1、数据准备
    //初始化顶点坐标与着色数据
    public void initVertexData()
    {
        //顶点坐标数据的初始化，顶点数量为3
        vCount=3;
        final float UNIT_SIZE=0.2f;//设置单位长度

        //顶点坐标数组
        //传送属性变量的值之前，需要按照顶点的次序依次将此属性变量对应的值送入缓冲
        //首先将顶点此项属性数据依次放入数组，这里是顶点坐标
        float vertices[]=new float[]
                {
                        -4*UNIT_SIZE,0,//第一个顶点X、Y、Z坐标值
                        0,0,-4*UNIT_SIZE,//第二个顶点X、Y、Z坐标值
                        0,4*UNIT_SIZE,0,0
                };
        //2、顶点缓冲对象
        //为形状坐标初始化顶点字节缓冲区，
        //在初始化阶段将顶点数据经过基本处理后送入顶点缓冲对象，在绘制每一帧想要的图形时就省去了顶点数据IO的麻烦
        //直接从顶点缓冲对象中获取顶点数据即可相比于每次绘制时单独将顶点数据送入GPU的方式，可以在一定程度上节省GPU的IO宽带，提高渲染效率
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
        mVertexBuffer = vbb.asFloatBuffer();//转换为浮点型缓冲
        mVertexBuffer.put(vertices);//在缓冲区内写入数据
        mVertexBuffer.position(0);//设置缓冲区起始位置

        //顶点的颜色数组
        float colors[]=new float[]
                {
                        1,1,1,0,
                        0,0,1,0,
                        0,1,0,0
                };
        //为形状坐标初始化顶点颜色缓冲区，开辟对应容量的缓冲
        ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
        cbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
        mColorBuffer = cbb.asFloatBuffer();//转换为浮点型缓冲
        mColorBuffer.put(colors);//在缓冲区内写入顶点数据
        mColorBuffer.position(0);//设置缓冲区起始位置
    }

    //创建，并初始化shader
    //  加载相应着色器脚本 --> 创建自定义渲染管线着色器程序 --> 获取程序中相关属性的引用
    public void initShader(MyTDView mv)
    {
        //加载顶点着色器的脚本内容
        mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
        //加载片元着色器的脚本内容
        mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
        //基于顶点着色器与片元着色器创建程序
        mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
        //获取程序中顶点位置属性引用id，获取了着色器中指定名称属性变量的引用值
        maPositionHandle = GLES20.glGetAttribLocation(
                mProgram,   ////采用的着色器程序id
                "aPosition"     //着色器中对应的属性变量名称
        );
        //获取程序中顶点颜色属性引用id
        maColorHandle= GLES20.glGetAttribLocation(mProgram, "aColor");
        //获取程序中总变换矩阵引用id
        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    }

    //自定义绘制3角形
    public void drawSelf()
    {
        //制定使用某套shader程序
        GLES20.glUseProgram(mProgram);
        //初始化变换矩阵
        Matrix.setRotateM(mMMatrix,0,0,0,1,0);
        //设置沿Z轴正向位移1
        Matrix.translateM(mMMatrix,0,0,0,1);
        //设置绕x轴旋转
        Matrix.rotateM(mMMatrix,0,xAngle,1,0,0);
        //调用C的native方法，glUniformMatrix4fv是默认将数组以列向量的形式存放在矩阵中的
        //通过一致变量muMVPMatrixHandle引用将一致变量值传入渲染管线（将4*4的矩阵传入管线）
        //注意：随一致变量类型的不同将值传入渲染管线的方法也有所不同，这些方法的名称都以glUniform开头
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0);
        //为画笔指定顶点位置数据，将顶点位置数据传送进渲染管线，以备渲染时在顶点着色器中使用
        //将已经放入缓冲中的数据批量传入渲染管线，
        //以备管线经过基本处理后将对应的值传递给顶点着色器中的属性变量
        GLES20.glVertexAttribPointer(
                maPositionHandle,   //顶点位置的属性引用
                3, //每顶点一组的数据个数（这里是X、Y、Z坐标，因此为3）
                GLES20.GL_FLOAT, //数据类型
                false,	//是否规格化  normalized
                3*4,   //每组数据的尺寸，这里每组3个浮点数值（X、Y、Z坐标）stride（幅度）
                        //每个浮点数4个字节，共3*4=12个字节
                mVertexBuffer	//存放了数据的缓冲
        );
        //为画笔指定顶点颜色数据，将顶点颜色数据传送进渲染管线，以备渲染时在顶点着色器中使用
        GLES20.glVertexAttribPointer(
                maColorHandle,
                4,
                GLES20.GL_FLOAT,
                false,
                4*4,
                mColorBuffer
        );
        //启用顶点位置数据数组
        GLES20.glEnableVertexAttribArray(maPositionHandle);
        //启用顶点着色数据数组
        GLES20.glEnableVertexAttribArray(maColorHandle);
        //执行绘制三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
    }

    /**
     * //产生最终变换矩阵的方法
     * 通过物体的3D变换矩阵、摄像机参数矩阵、投影矩阵计算产生最终总变换矩阵
     */
    public static float[] getFianlMatrix(float[] spec)
    {
        mMVPMatrix=new float[16];//初始化总变换矩阵
        //将两个4x4矩阵相乘在一起，并将结果存储在第三个4x4中
        /**
         * @param result保存结果的float数组。
         * @param resultOffset结果所在的结果数组的偏移量存储。
         * @param lhs保存左侧矩阵的float数组。
         * @param lhsOffset存储lhs的lhs数组的偏移量
         * @param rhs包含右侧矩阵的float数组。
         * @param rhsOffset存储rhs的rhs数组的偏移量。
         */
        Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
        return mMVPMatrix;//返回总变换矩阵
    }
}